/* gtrans - Translation helped by Google
 * Copyright (C) 2011 Nagy Attila
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <json-glib/json-glib.h>
#include <json-glib/json-gobject.h>
#include <locale.h>
#include <glib/gi18n.h>
#include <syslog.h>
#include "gtrans.h"
#include "settings.h"

using namespace std;

GTrans::GTrans ()
{
    languages["af"] = _("Afrikaans");
    languages["sq"] = _("Albanian");
    languages["ar"] = _("Arabic");
    languages["hy"] = _("Armenian");
    languages["az"] = _("Azerbaijani");
    languages["eu"] = _("Basque");
    languages["be"] = _("Belarusian");
    languages["bg"] = _("Bulgarian");
    languages["ca"] = _("Catalan");
    languages["zh-CN"] = _("Chinese (Simplified)");
    languages["zh-TW"] = _("Chinese (Traditional)");
    languages["hr"] = _("Croatian");
    languages["cs"] = _("Czech");
    languages["da"] = _("Danish");
    languages["nl"] = _("Dutch");
    languages["en"] = _("English");
    languages["et"] = _("Estonian");
    languages["tl"] = _("Filipino");
    languages["fi"] = _("Finnish");
    languages["fr"] = _("French");
    languages["gl"] = _("Galician");
    languages["ka"] = _("Georgian");
    languages["de"] = _("German");
    languages["el"] = _("Greek");
    languages["ht"] = _("Haitian Creole");
    languages["iw"] = _("Hebrew");
    languages["hi"] = _("Hindi");
    languages["hu"] = _("Hungarian");
    languages["is"] = _("Icelandic");
    languages["id"] = _("Indonesian");
    languages["ga"] = _("Irish");
    languages["it"] = _("Italian");
    languages["ja"] = _("Japanese");
    languages["ko"] = _("Korean");
    languages["la"] = _("Latin");
    languages["lv"] = _("Latvian");
    languages["lt"] = _("Lithuanian");
    languages["mk"] = _("Macedonian");
    languages["ms"] = _("Malay");
    languages["mt"] = _("Maltese");
    languages["no"] = _("Norwegian");
    languages["fa"] = _("Persian");
    languages["pl"] = _("Polish");
    languages["pt"] = _("Portuguese");
    languages["ro"] = _("Romanian");
    languages["ru"] = _("Russian");
    languages["sr"] = _("Serbian");
    languages["sk"] = _("Slovak");
    languages["sl"] = _("Slovenian");
    languages["es"] = _("Spanish");
    languages["sw"] = _("Swahili");
    languages["sv"] = _("Swedish");
    languages["th"] = _("Thai");
    languages["tr"] = _("Turkish");
    languages["uk"] = _("Ukrainian");
    languages["ur"] = _("Urdu");
    languages["vi"] = _("Vietnamese");
    languages["cy"] = _("Welsh");
    languages["yi"] = _("Yiddish");

    openlog (PACKAGE, LOG_PID | LOG_CONS, LOG_USER);
    g_log_set_handler (G_LOG_DOMAIN, (GLogLevelFlags) (G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION),
                       GTrans::handle_log, this);

    m_refSettings = Gio::Settings::create (CONF_HOME);
    bool m_enabled = m_refSettings->get_boolean (CONF_ENABLED);
    m_refSettings->signal_changed ().connect (sigc::mem_fun (*this, &GTrans::on_settings_changed));

    m_refStatusIcon = Gtk::StatusIcon::create_from_file (m_enabled ? ICON_PATH_ON : ICON_PATH_OFF);
    set_tooltip_text ();
    m_refStatusIcon->signal_popup_menu ().connect (sigc::mem_fun (*this, &GTrans::on_status_icon_popup_menu));
    m_refStatusIcon->signal_activate ().connect (sigc::mem_fun (*this, &GTrans::on_status_icon_activate));
    m_refStatusIcon->set_visible ();

    m_refActionGroup = Gtk::ActionGroup::create ();
    m_refActionGroup->add (Gtk::Action::create ("Enable", m_enabled ? _("Turn off") : _("Turn on")),
                           sigc::mem_fun (*this, &GTrans::on_enabled_menu_item_activate));
    m_refActionGroup->add (Gtk::Action::create ("Preferences", Gtk::Stock::PREFERENCES),
                           sigc::mem_fun (*this, &GTrans::on_preferences_menu_item_activate));
    m_refActionGroup->add (Gtk::Action::create ("About", Gtk::Stock::ABOUT),
                           sigc::mem_fun (*this, &GTrans::on_about_menu_item_activate));
    m_refActionGroup->add (Gtk::Action::create ("Exit", Gtk::Stock::QUIT),
                           sigc::mem_fun (*this, &GTrans::on_exit_menu_item_activate));

    m_refUIManager = Gtk::UIManager::create ();
    m_refUIManager->insert_action_group (m_refActionGroup);

    Glib::ustring ui_info =
        "<ui>"
        "  <popup name='PopupMenu'>"
        "    <menuitem action='Enable'/>"
        "    <separator/>"
        "    <menuitem action='Preferences'/>"
        "    <menuitem action='About'/>"
        "    <menuitem action='Exit'/>"
        "  </popup>"
        "</ui>";
    m_refUIManager->add_ui_from_string (ui_info);

    Gtk::Clipboard::get (GDK_SELECTION_PRIMARY)->signal_owner_change ().connect (sigc::mem_fun (*this, &GTrans::on_clipboard_owner_change));

    m_notification = NULL;
    notify_init (PACKAGE);
}

GTrans::~GTrans ()
{
    if (m_notification) g_object_unref (G_OBJECT (m_notification));
    notify_uninit ();
    closelog ();
}

void GTrans::on_status_icon_popup_menu (guint button, guint32 activate_time)
{
    Gtk::Menu *menu = static_cast<Gtk::Menu *> (m_refUIManager->get_widget ("/PopupMenu"));
    m_refStatusIcon->popup_menu_at_position (*menu, button, activate_time);
}

void GTrans::on_status_icon_activate ()
{
    //g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "on_status_icon_activate ()");
}

void GTrans::on_enabled_menu_item_activate ()
{
    bool enabled = m_refSettings->get_boolean (CONF_ENABLED);
    m_refSettings->set_boolean (CONF_ENABLED, not enabled);
}

void GTrans::on_preferences_menu_item_activate ()
{
    Settings settings (languages/*, m_refSettings*/);
    settings.run ();
}

void GTrans::on_exit_menu_item_activate ()
{
    Gtk::Main::quit ();
}

void GTrans::on_about_menu_item_activate ()
{
    Gtk::AboutDialog about_dialog;
    about_dialog.set_program_name (PACKAGE_NAME);
    about_dialog.set_version (PACKAGE_VERSION);
    about_dialog.set_copyright ("Copyright \xc2\xa9 2011 Nagy Attila");
    about_dialog.set_comments (_("Translation helped by Google."));
    about_dialog.set_license (_("This program is free software: you can redistribute it and/or modify it "
                                "under the terms of the GNU General Public License as published by the Free "
                                "Software Foundation, either version 3 of the License, or (at your option) "
                                "any later version.\n\nThis program is distributed in the hope that it will be "
                                "useful, but WITHOUT ANY WARRANTY; without even the implied warranty of  "
                                "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General "
                                "Public License for more details.\n\nYou should have received a copy of the GNU "
                                "General Public License along with this program. If not, see <http://www."
                                "gnu.org/licenses/>."));
    //about_dialog.set_license_type (Gtk::LICENSE_LGPL_3_0);
    about_dialog.set_wrap_license (true);
    about_dialog.set_website ("http://gtrans.sf.net/"); // TODO PACKAGE_URL
    about_dialog.set_website_label (_("Homepage"));
    vector<Glib::ustring> authors;
    authors.push_back ("Nagy Attila <hu.attila.nagy@gmail.com>");
    about_dialog.set_authors (authors);
    vector<Glib::ustring> documenters;
    documenters.push_back ("Nagy Attila <hu.attila.nagy@gmail.com>");
    about_dialog.set_documenters (documenters);
    vector<Glib::ustring> artists;
    artists.push_back ("Nagy Attila <hu.attila.nagy@gmail.com>");
    about_dialog.set_artists (artists);
    //about_dialog.set_translator_credits (_("translator_credits")); // TODO
    about_dialog.set_logo (Gdk::Pixbuf::create_from_file (ICON_PATH_LOGO));
    about_dialog.run ();
}

void GTrans::on_clipboard_owner_change (GdkEventOwnerChange *event)
{
    if (m_refSettings->get_boolean (CONF_ENABLED))
    {
        Gtk::Clipboard::get (GDK_SELECTION_PRIMARY)->request_contents (CLIPBOARD_TARGET, sigc::mem_fun (*this, &GTrans::on_clipboard_received));
    }
}

void GTrans::on_clipboard_received (const Gtk::SelectionData& selection_data)
{
    string target = selection_data.get_target ();
    if (target == CLIPBOARD_TARGET)
    {
        source_text_act = selection_data.get_data_as_string ();
        //g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Source text: %s", source_text_act.c_str ());

        trim (source_text_act);
        if (!source_text_act.empty ())
        {
            translate_google ();
        }
        else
        {
            //g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "empty selection");
        }
    }
}

void GTrans::translate_google ()
{
    source_lang_listen = target_lang_listen = false;
    SoupSession *session = soup_session_async_new_with_options (
#ifdef HAVE_LIBSOUP_GNOME
                               SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
#endif
                               //SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
                               //SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
                               SOUP_SESSION_USER_AGENT, PACKAGE_STRING,
                               //SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
                               NULL);

    string uri (GOOGLE_URI);
    Glib::ustring key = m_refSettings->get_string (CONF_GOOGLE_API_KEY);
    uri += "key=";
    uri += key;
    uri += "&q=";

    char *encoded_text = soup_uri_encode (source_text_act.c_str (), NULL);
    //g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Encoded text: %s", encoded_text);
    uri += encoded_text;
    free (encoded_text);

    source_lang_act = get_source_language ();
    if (source_lang_act != AUTO_DETECT_SOURCE_LANG)
    {
        uri += "&source=";
        uri += source_lang_act;
    }

    target_lang_act = get_target_language ();
    uri += "&target=";
    uri += target_lang_act;

    //g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "URI: %s", uri.c_str ());

    SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, uri.c_str ());
    soup_session_queue_message (session, msg, GTrans::on_translated_google, this);
}

void GTrans::on_translated_google (SoupSession *session, SoupMessage *msg, gpointer user_data)
{
    SoupMessageBody *body;
    g_object_get (G_OBJECT (msg), "response-body", &body, NULL);

    if (SOUP_STATUS_IS_CLIENT_ERROR (msg->status_code))
    {
        g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
               "%s HTTP error: %d (%s)\n%s", LOG_CATEGORY_HTTP400, msg->status_code, msg->reason_phrase, body->data);
        return;
    }
    else if (SOUP_STATUS_IS_SERVER_ERROR (msg->status_code))
    {
        g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
               "%s HTTP error: %d (%s)\n%s", LOG_CATEGORY_HTTP500, msg->status_code, msg->reason_phrase, body->data);
        return;
    }
    else if (!(SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)))
    {
        g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
               "%s HTTP error: %d (%s)\n%s", LOG_CATEGORY_NETWORK, msg->status_code, msg->reason_phrase, body->data);
        return;
    }

    //g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "JSON response:\n%s", body->data);

    JsonParser *parser = json_parser_new ();
    GError *error = NULL;
    if (json_parser_load_from_data (parser, body->data, -1, &error) == TRUE)
    {
        JsonNode *root_node = json_parser_get_root (parser);
        if (JSON_NODE_HOLDS_OBJECT (root_node))
        {
            JsonObject *root_object = json_node_get_object (root_node);
            JsonNode * data_node = json_object_get_member (root_object, "data");
            if (data_node != NULL)
            {
                if (JSON_NODE_HOLDS_OBJECT (data_node))
                {
                    JsonObject *data_object = json_node_get_object (data_node);
                    JsonNode * translations_node = json_object_get_member (data_object, "translations");
                    if (translations_node != NULL)
                    {
                        if (JSON_NODE_HOLDS_ARRAY (translations_node))
                        {
                            JsonArray *translations_array = json_node_get_array (translations_node);
                            json_array_foreach_element (translations_array, GTrans::iterate_translations, user_data);
                        }
                        else
                        {
                            g_warning ("'translations' node is not an array");
                        }
                    }
                    else
                    {
                        g_warning ("'data' does not contain 'translations' member");
                    }
                }
                else
                {
                    g_warning ("'data' node is not an object");
                }
            }
            else
            {
                g_warning ("'root' does not contain 'data' member");
            }
        }
        else
        {
            g_warning ("'root' node is not an object");
        }
    }
    else
    {
        g_warning ("json_parser_load_from_data FAILED ... \"%s\"", error->message);
        g_error_free (error);
    }
    g_object_unref (parser);
}

void GTrans::iterate_translations (JsonArray *array, guint index_, JsonNode *element_node, gpointer user_data)
{
    if (JSON_NODE_HOLDS_OBJECT (element_node))
    {
        JsonObject *element_object = json_node_get_object (element_node);
        JsonNode * translated_text_node = json_object_get_member (element_object, "translatedText");
        if (translated_text_node != NULL)
        {
            if (JSON_NODE_HOLDS_VALUE (translated_text_node))
            {
                GTrans *self = (GTrans *) user_data;
                self->target_text_act = json_node_get_string (translated_text_node);
                //g_debug ("translated text: ", translated_text);
                if (self->source_lang_act == AUTO_DETECT_SOURCE_LANG)
                {
                    JsonNode * detected_source_language_node = json_object_get_member (element_object, "detectedSourceLanguage");
                    if (detected_source_language_node != NULL)
                    {
                        if (JSON_NODE_HOLDS_VALUE (detected_source_language_node))
                        {
                            self->source_lang_act = json_node_get_string (detected_source_language_node);
                        }
                        else
                        {
                            g_warning ("'detectedSourceLanguage' is not a value");
                            return;
                        }
                    }
                    else
                    {
                        g_warning ("'element_object' does not contain 'detectedSourceLanguage' member");
                        return;
                    }
                }
                self->show_translated_text ();
                self->speak ();
            }
            else
            {
                g_warning ("'translated_text_node' is not a value");
            }
        }
        else
        {
            g_warning ("'element_object' does not contain 'translatedText' member");
        }
    }
    else
    {
        g_warning ("'translations' array member node (index: %d) is not an object", index_);
    }
}

void GTrans::show_translated_text ()
{
    //g_log ("'show_translated_text' : %s", target_text_act);
    GError *error = NULL;

    Glib::ustring summary;
    summary += languages[source_lang_act];
    summary += " - ";
    summary += languages[target_lang_act];

    if (m_notification == NULL)
    {
        m_notification = notify_notification_new (summary.c_str (), target_text_act.c_str (), ICON_PATH_LOGO, NULL);
        notify_notification_attach_to_status_icon (m_notification, m_refStatusIcon->gobj ());
        notify_notification_set_timeout (m_notification, NOTIFY_EXPIRES_DEFAULT);
        notify_notification_set_category (m_notification, PACKAGE);
        notify_notification_set_urgency (m_notification, NOTIFY_URGENCY_LOW);
    }
    else
    {
        if (notify_notification_update (m_notification, summary.c_str (), target_text_act.c_str (), ICON_PATH_LOGO) == FALSE)
        {
            g_critical ("notify_notification_update failed");
            return;
        }
    }
    if (notify_notification_show (m_notification, &error) == FALSE)
    {
        g_critical ("notify_notification_show failed: \"%s\"", error->message);
        g_error_free (error);
    }
}

void GTrans::speak ()
{
    if (!source_lang_listen && m_refSettings->get_boolean (CONF_LISTEN_SOURCE))
    {
        source_lang_listen = true;
        speak (source_text_act, source_lang_act);
    }
    else if (!target_lang_listen && m_refSettings->get_boolean (CONF_LISTEN_TARGET))
    {
        target_lang_listen = true;
        speak (target_text_act, target_lang_act);
    }
}

void GTrans::speak (string& text, Glib::ustring lang)
{
    if (text.length () > MAX_GOOGLE_SPEAKER_LENGTH || text.empty ())
    {
        g_debug ("text too long or short to speak");
        return;
    }

    string uri (GOOGLE_SPEAKER_URI);
    uri += "&q=";

    char *encoded_text = soup_uri_encode (text.c_str (), NULL);
    uri += encoded_text;
    free (encoded_text);

    uri += "&tl=";
    uri += lang;

    play_uri (uri.c_str ());
}

gboolean GTrans::bus_call (GstBus *bus, GstMessage *msg, gpointer user_data)
{
    //g_debug ("bus_call: %d", GST_MESSAGE_TYPE (msg));

    switch (GST_MESSAGE_TYPE (msg))
    {
    case GST_MESSAGE_EOS:
    {
        //g_debug ("End-of-stream");
        GTrans *self = (GTrans *) user_data;
        gst_element_set_state (GST_ELEMENT (self->pipeline), GST_STATE_NULL);
        gst_object_unref (GST_OBJECT (self->pipeline));
        if (self->source_lang_listen)
        {
            self->speak ();
        }
        break;
    }
    case GST_MESSAGE_ERROR:
    {
        GError *err;
        gst_message_parse_error (msg, &err, NULL);
        g_warning ("%s", err->message);
        g_error_free (err);
        gst_object_unref (GST_OBJECT (((GTrans *) user_data)->pipeline));
        break;
    }
    default:
        break;
    }

    return true;
}

void GTrans::play_uri (const char *uri)
{
    //g_debug ("Play URI: %s", uri);

    string pipeline_description = "souphttpsrc location=\"";
    pipeline_description += uri;
    pipeline_description += "\" user-agent=\"";
    pipeline_description += GOOGLE_SPEAKER_USER_AGENT;
    pipeline_description += "\" ! mp3parse ! mad ! autoaudiosink";

    GError *err = NULL;
    pipeline = gst_parse_launch (pipeline_description.c_str (), &err);
    if (err)
    {
        g_warning ("%s", err->message);
        g_error_free (err);
    }
    if (pipeline)
    {
        GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
        gst_bus_add_watch (bus, GTrans::bus_call, (gpointer) this);
        gst_object_unref (bus);
        gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
    }
}

void GTrans::on_settings_changed (const Glib::ustring& key)
{
    if (key == CONF_ENABLED)
        on_conf_enabled_changed ();
    else if (key == CONF_LANG_SOURCE)
        on_conf_source_language_changed ();
    else if (key == CONF_LANG_TARGET)
        on_conf_target_language_changed ();
}

void GTrans::on_conf_enabled_changed ()
{
    bool enabled = m_refSettings->get_boolean (CONF_ENABLED);
    Gtk::MenuItem *menuitem = static_cast<Gtk::MenuItem *> (m_refUIManager->get_widget ("/PopupMenu/Enable"));
    menuitem->set_label (enabled ? _("Turn off") : _("Turn on"));
    m_refStatusIcon->set_from_file (enabled ? ICON_PATH_ON : ICON_PATH_OFF);
}

void GTrans::on_conf_source_language_changed ()
{
    set_tooltip_text ();
}

void GTrans::on_conf_target_language_changed ()
{
    set_tooltip_text ();
}

void GTrans::trim (string& str)
{
    string whitespaces (" \t\f\v\n\r");
    str = str.erase (str.find_last_not_of (whitespaces) + 1);
    str = str.erase (0, str.find_first_not_of (whitespaces));
}

Glib::ustring GTrans::get_source_language ()
{
    Glib::ustring source_language = m_refSettings->get_string (CONF_LANG_SOURCE);
    if (source_language.empty ())
    {
        source_language = AUTO_DETECT_SOURCE_LANG;
        m_refSettings->set_string (CONF_LANG_SOURCE, source_language);
    }
    return source_language;
}

Glib::ustring GTrans::get_target_language ()
{
    Glib::ustring target_language = m_refSettings->get_string (CONF_LANG_TARGET);
    if (target_language.empty ())
    {
        target_language = setlocale (LC_ALL, NULL);
        target_language = target_language.substr (0, 2);
        m_refSettings->set_string (CONF_LANG_TARGET, target_language);
    }
    return target_language;
}

void GTrans::set_tooltip_text ()
{
    string tooltip_text;
    tooltip_text += _("Translation helped by Google");
    tooltip_text += "\n";
    tooltip_text += "(";
    Glib::ustring source_language = get_source_language ();
    tooltip_text += (source_language == AUTO_DETECT_SOURCE_LANG ? _("Detect language") : languages[source_language]);
    tooltip_text += " - ";
    tooltip_text += languages[get_target_language ()];
    tooltip_text += ")";
    m_refStatusIcon->set_tooltip_text (tooltip_text);
}

void GTrans::handle_log (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
{
    g_log_default_handler (log_domain, log_level, message, user_data);
    syslog (LOG_INFO, message);
    if (log_level & G_LOG_LEVEL_WARNING)
    {
        char info[ERROR_INFO_LENGTH + 1];
        GTrans::error_info (message, info);

        GTrans *self = (GTrans *) user_data;
        NotifyNotification *notification = notify_notification_new (PACKAGE, info, ICON_PATH_WARN, NULL);
        notify_notification_attach_to_status_icon (notification, self->m_refStatusIcon->gobj ());
        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
        notify_notification_set_category (notification, PACKAGE);
        notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL);
        notify_notification_show (notification, NULL);

        g_object_unref (G_OBJECT (notification));
    }
    else if (log_level & G_LOG_LEVEL_CRITICAL)
    {
        Gtk::MessageDialog dialog (message, false, Gtk::MESSAGE_ERROR);
        dialog.set_title (PACKAGE);
        dialog.run ();
        abort ();
    }
}

void GTrans::error_info (const char *message, char *info)
{
    const char *c;
    c = strstr (message, LOG_CATEGORY_HTTP400);
    if (c == message)
    {
        strncpy (info, _("Bad request. Check your API key!"), ERROR_INFO_LENGTH);
        return;
    }
    c = strstr (message, LOG_CATEGORY_HTTP500);
    if (c == message)
    {
        strncpy (info, _("Service temporarily unavailable. Try again later!"), ERROR_INFO_LENGTH);
        return;
    }
    c = strstr (message, LOG_CATEGORY_NETWORK);
    if (c == message)
    {
        strncpy (info, _("Network problem. Check your connection!"), ERROR_INFO_LENGTH);
        return;
    }
    c = strstr (message, LOG_CATEGORY_NOTIFY);
    if (c == message)
    {
        strncpy (info, _("Notification error."), ERROR_INFO_LENGTH);
        return;
    }
    strncpy (info, message, ERROR_INFO_LENGTH);
}
